package graphs;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.List;
import tester.Example;
import tester.cobertura.Instrument;
import lists.MyArrayList;
import lists.UnaryFunction;

/**
 * <h1> Title : Path </h1>
 * <p> Description : Generalization of the route concept. It is a MyArrayList of 
 * T types. Cloneable. </p>
 * 
 * @author Adam Blackwell & Ryan Cassidy with influence from Jean-Michel Garnier
 * @since February 22nd 2009
 * @version 1.6
 */
@Instrument @Example public class Path<T> implements Cloneable {
  /**
   * <p> List which contains the vertices. </p> 
   */
  private MyArrayList<Vertex> verticesList;

  /**
   * <p> Default Constructor : </p> 
   */
  public Path() {
    verticesList = new MyArrayList<Vertex>();
  }
  
  /**
   * <p> Constructor : With List of nodes. </p> 
   * 
   * @param list
   */
  public Path(MyArrayList<Vertex> list) {
    verticesList = list;
  }
  
  /**
   * <p> GetLength : returns the length of the verticeslist. </p> 
   * 
   * @return The length of the Path.
   */
  public int getLength() {
    return verticesList.size() - 1;
  }
  
  /**
   * <p> AddVertex : adds a vertex to be the vertices list. </p> 
   * 
   * @param vertex should be cloneable
   * @return this path
   */
  public Path<T> addVertex(Vertex vertex) {
    verticesList.add(vertex);
    return this;
  }
  
  /**
   * <p> AddPath : Adds a path to the vertices list (and returns the
   * modified path object? </p>
   * 
   * TODO Ryan, this is just append... :P 
   * 
   * @param path
   * @return this path
   */
  public Path<T> addPath(Path<Vertex> path) {
    verticesList.addAll(path.verticesList);
    return this;
  }
  
  /**
   * <p> Validate: Checks if the list of vertexes is a valid path,
   * by checking if each vertex is a neighbor of its predecessor </p> 
   * 
   * @return whether the path is valid
   */
  public boolean validate() {
    // Creates a final copy of the current path instance, so nothing screws up.
    final Path<Vertex> p = this.clone();
    boolean valid = true;
    // Checks if the vertices list is empty.
    while (this.verticesList.size() >= 2) {
      // Ormap checks if any of the current vertex's neighbor links 
      // go to the next vertex.
      valid = valid || this.verticesList.get(0).neighbors.
        ormap(new UnaryFunction<Boolean, Link>() {
          public Boolean f(Link l) {
            // Checks if the selected link goes to the next vertex.
            return l.getDestinationVertex().equals(p.get(1));
          };
        }); 
      // Mutates the list by removing the first element that we know is valid.
      p.verticesList.remove(0);
    }
    // Returns the end result of the ormap.
    return valid;
  }
  
  /**
   * <p> GetWeight : Goes through the list of nodes and pulls the link
   * from one to the other totaling them as it goes. </p> 
   * 
   * @return the total weight
   */
  public int getWeight() {
    // The total weight.
    int weight = 0;
    // The first vertex is set at the start.
    Vertex v1 = this.verticesList.get(0);
    for (int i = 1; i < this.getLength(); i++) {
      // Adds the next link's weight to the total weight.
      Vertex v2 = this.verticesList.get(i);
      weight += v1.weightToNeightbor(v2);
      // The second vertex is assigned to the first place holder
      // to minimize assignments.
      v1 = v2;
    }
    // When done, return the total weight.
    return weight;
  }
  
  /**
   * <p> Clone. clone the path, all the objects in the path should be
   * cloneables </p> 
   * 
   * TODO Ryan, look at the javadoc, I should use this somewhere where I don't,
   * but for now its here when we need it.
   * 
   * @return the cloned path
   */
  @SuppressWarnings("unchecked") public Path<Vertex> clone() {
    Path<Vertex> newInstance = null;
    try {
      newInstance = (Path<Vertex>) super.clone();
    } catch (CloneNotSupportedException e) {
      // the objects in the path are supposed to be cloneable
      e.printStackTrace();
    }
    newInstance.verticesList = (MyArrayList<Vertex>) verticesList.clone();
    return newInstance;
  }
  
  /**
   * <p> ToString : Returns a list of all the vertices of the path separated
   * by - i.e. Path[A, B, C] --> "A-B-C" </p> 
   * 
   * @return the String representation of the path
   */
  public String toString() {
    String s = new String();
    ListIterator<Vertex> iter = verticesList.listIterator();
    if (iter.hasNext()) {
      Object item = iter.next();
      s += item.toString();
    }
    while (iter.hasNext()) {
      Object item = iter.next();
      s += "-" + item.toString();
    }
    return s;
  }
  
  /**
   * <p> Get: Returns the object at position index in the path. <p> 
   * 
   * @param index
   */
  public Vertex get(int index) {
    return verticesList.get(index);
  }
  
  /**
   * GetLast: Returns last object of the path.
   */
  public Vertex getLast() {
    return verticesList.get(verticesList.size() - 1);
  }
  
  /**
   * GetVertices: Returns the vertices list.
   */
  public MyArrayList<Vertex> getVerticesList() {
    return verticesList;
  }
}
